package com.atguigu.gmall.gateway.fillter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;

/**
 * @author: 李旭
 * @create: 2021-09-06 15:37
 *
 *    自定义全局过滤器
 *       服务器一启动就是10大过滤器
 *         SpringGateWay  默认有9大过滤器  +  自定义全局过滤器   = 10大过滤器
 *        预计是第4~6个执行  中间执行环节
 **/
@Component  //实例化过滤器
public class LoginGlobalFilter implements GlobalFilter, Ordered {

    public static final String TOKEN = "token";
    public static final String USERTEMPID = "userTempId";
    //用户登录
    public static final String USER_LOGIN_KEY_PREFIX = "user:login:";
    //路径匹配
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public static final String LOGINURL =
            "http://passport.gmall.com/login.html?originUrl=";

    @Value("${url.all}")
    public String[] urlAll;
    @Autowired
    private RedisTemplate redisTemplate;
    //过滤器执行方法
    //参数2：GatewayFilterChain 过滤器链  长度 10个
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //0：准备工作
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //1:判断用户是否登录
        //1.1:获取Redis的用户ID
        String userId = getUserId(request);
        //2:校验请求是否为内部资源 http://api.gmall.com/api/product/inner/haha/hehe
        String path = request.getURI().getPath();
        //request.getURI().getScheme()  http
        //request.getURI().getRawSchemeSpecificPart() //www.suning.com/?utm_source=baidu&utm_medium=brand&utm_campaign=title&utm_term=brand

        System.out.println("path:" + path);
        if(antPathMatcher.match("/**/inner/**",path)){
            //是内部资源 不允许访问  普通消费者 是不知道 不知道内资的路径  公司离职员工
            return out(response,ResultCodeEnum.PERMISSION);
        }
        //3、校验用户是否登录
        //3.1: 首页 搜索 详情  购物车 可以不登录继续访问
        // 结算 提交订单 支付 用户中心 必须登录
        //3.2 异步  /api/order/auth/submitOrder  提交订单
        if(antPathMatcher.match("/**/auth/**",path)
                   && StringUtils.isEmpty(userId)){
            //当前请求是必须登录才能访问 同时还未登录
            //返回值 未登录  data 未登录状态  弹出层
            return out(response,ResultCodeEnum.LOGIN_AUTH);
        }
        //3.3 同步        trade.html,pay.html,order.html
        //   /addCart.html
        for (String url : urlAll) {
            if(path.indexOf(url) != -1 && StringUtils.isEmpty(userId)){
                //当前请求是必须登录才能访问 同时还未登录
                try {
                    //重定向到登录页面  303 seeother
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    //重定向的URL地址
                    response.getHeaders().add(HttpHeaders.LOCATION,
                            LOGINURL +
                                    URLEncoder.encode(request.getURI().toString(),"UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return response.setComplete();//完成
            }
        }
        //传递真实用户ID
        //request 老请求
        //request.getHeaders().add("userId",userId);//报错
        //老请求产生了新的请求   改变或变化
        request.mutate().header("userId",userId);//传递真实用户ID
        //传递临时用户ID
        String userTempId = getUserTempId(request);
        request.mutate().header("userTempId",userTempId);
        //放行
        return chain.filter(exchange);
    }

    //获取临时用户ID
    private String getUserTempId(ServerHttpRequest request) {
        //1:从请求头中获取临时用户Id
        String userTempId = request.getHeaders().getFirst(USERTEMPID);
        if(StringUtils.isEmpty(userTempId)){
            //2:从Cookie中获取
            HttpCookie httpCookie = request.getCookies().getFirst(USERTEMPID);
            if(null != httpCookie){
                userTempId = httpCookie.getValue();
            }
        }

        return userTempId;

    }

    //提取出统一返回值方法
    public Mono<Void> out(ServerHttpResponse response,ResultCodeEnum resultCodeEnum){
        Result<Object> result = Result.build(null, resultCodeEnum);
        //json字符串
        String r = JSONObject.toJSONString(result);
        DataBufferFactory dataBufferFactory = response.bufferFactory();
        DataBuffer dataBuffer = dataBufferFactory.wrap(r.getBytes());
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE,
                MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));
    }

    //获取Redis的用户ID
    private String getUserId(ServerHttpRequest request) {
        //1:获取出令牌
        //1.1:可能在请求头中   Ajax异步请求  默认情况下不携带Cookie 令牌放在请求头中
        String token = request.getHeaders().getFirst(TOKEN);
        if(StringUtils.isEmpty(token)){
            //1.2:可能在Cookie中  同步请求  携带Cookie过来的  令牌就在Cookie中
            HttpCookie httpCookie = request.getCookies().getFirst(TOKEN);
            if(null != httpCookie){
                token = httpCookie.getValue();
            }
        }
       //判断是否有令牌
        if(!StringUtils.isEmpty(token)){
            //用户的请求中有令牌  通过令牌获取Redis缓存中的用户ID
            //以Redis缓存为准 缓存中有用户的信息 才能证明是登录了
            if(redisTemplate.hasKey(USER_LOGIN_KEY_PREFIX + token)){
                return (String) redisTemplate.opsForValue().
                        get(USER_LOGIN_KEY_PREFIX + token);
            }
        }
        //未登录
        return null;
    }

    //排序
    @Override
    public int getOrder() {
        return 0;
    }
}
